﻿using System;
using Microsoft.Xna.Framework;

namespace BlockeyEssentials.Drawing
{
    public class ColorHSL
    {
        public ColorHSL(double h, double s, double l)
            : this(h, s, l, 1.0f)
        { }

        public ColorHSL(double h, double s, double l, double t)
        {
            m_dHue = h;
            m_dSaturation = s;
            m_dLightness = l;
            m_dTransparency = t;

            ConvertHSLToRGB();
        }

        #region Methods

        public void NextColor()
        {
            double oHue = 1.0;
            double oTolerance = 0.002;
            while (Hue < oHue - oTolerance)
            {
                oHue -= 0.125;
            }

            Hue = oHue + 0.125;
        }

        public void PreviousColor()
        {
            double oHue = 1.0;
            double oTolerance = 0.002;
            while (Hue < oHue - oTolerance)
            {
                oHue -= 0.125;
            }

            Hue = oHue - 0.125;
        }

        #region Conversion Methods

        public void ConvertRGBtoHSL(Color rgb)
        {
            RGB_To_HSL(rgb);
            ConvertHSLToRGB();
        }

        private void ConvertHSLToRGB()
        {
            m_oColorRGB = HSL_To_RGB(Hue, Saturation, Lightness, Transparency);
        }

        public static Color HSL_To_RGB(double h, double sl, double l, double t)
        {
            double v, r, g, b;

            r = l;   // default to gray
            g = l;
            b = l;
            v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
            if (v > 0)
            {
                double m;
                double sv;
                int sextant;
                double fract, vsf, mid1, mid2;

                m = l + l - v;
                sv = (v - m) / v;
                h *= 6.0f;
                sextant = (int)h;
                fract = h - sextant;
                vsf = v * sv * fract;
                mid1 = m + vsf;
                mid2 = v - vsf;
                switch (sextant)
                {
                    case 0:
                        r = v;
                        g = mid1;
                        b = m;
                        break;
                    case 1:
                        r = mid2;
                        g = v;
                        b = m;
                        break;
                    case 2:
                        r = m;
                        g = v;
                        b = mid1;
                        break;
                    case 3:
                        r = m;
                        g = mid2;
                        b = v;
                        break;
                    case 4:
                        r = mid1;
                        g = m;
                        b = v;
                        break;
                    case 5:
                        r = v;
                        g = m;
                        b = mid2;
                        break;
                }
            }

            var oColorRGB = Color.White;
            oColorRGB.R = Convert.ToByte(r * 255.0f);
            oColorRGB.G = Convert.ToByte(g * 255.0f);
            oColorRGB.B = Convert.ToByte(b * 255.0f);
            oColorRGB.A = Convert.ToByte(t * 255.0f);

            return oColorRGB;
        }

        public static ColorHSL RGB_To_HSL(Color rgb)
        {
            var HSL = new ColorHSL(0, 0, 0);
            double r = rgb.R / 255.0;
            double g = rgb.G / 255.0;
            double b = rgb.B / 255.0;
            double v;
            double m;
            double vm;
            double r2, g2, b2;

            v = Math.Max(r, g);
            v = Math.Max(v, b);
            m = Math.Min(r, g);
            m = Math.Min(m, b);

            HSL.Lightness = (m + v) / 2.0;
            if (HSL.Lightness > 0.0)
            {
                vm = v - m;
                HSL.Saturation = vm;

                if (HSL.Saturation > 0.0)
                {
                    HSL.Saturation /= (HSL.Lightness <= 0.5) ?
                        (v + m) : (2.0 - v - m);

                    r2 = (v - r) / vm;
                    g2 = (v - g) / vm;
                    b2 = (v - b) / vm;

                    if (r == v)
                        HSL.Hue = (g == m ? 5.0 + b2 : 1.0 - g2);
                    else if (g == v)
                        HSL.Hue = (b == m ? 1.0 + r2 : 3.0 - b2);
                    else
                        HSL.Hue = (r == m ? 3.0 + g2 : 5.0 - r2);

                    HSL.Hue /= 6.0;
                }
            }

            return HSL;
        }

        #endregion

        #endregion

        #region Properties

        public double Hue
        {
            get { return m_dHue; }
            set
            {
                if (value >= 1)
                    value = value - 1;
                while (value < 0)
                    value = value + 1;
                m_dHue = value;

                ConvertHSLToRGB();
            }
        }

        public double Saturation
        {
            get { return m_dSaturation; }
            set
            {
                m_dSaturation = MathHelper.Clamp((float)value, 0.0f, 1.0f);
                ConvertHSLToRGB();
            }
        }

        public double Lightness
        {
            get { return m_dLightness; }
            set
            {
                m_dLightness = MathHelper.Clamp((float)value, 0.0f, 1.0f);
                ConvertHSLToRGB();
            }
        }

        public double Transparency
        {
            get { return m_dTransparency; }
            set
            {
                m_dTransparency = MathHelper.Clamp((float)value, 0.0f, 1.0f);
                ConvertHSLToRGB();
            }
        }

        public Color ColorRGB
        {
            get { return m_oColorRGB; }
        }

        public Color ColorClean
        {
            get
            {
                ColorHSL oColor = new ColorHSL(Hue, 1.0f, 0.5f);
                return oColor.ColorRGB;
            }
        }

        #endregion

        double m_dHue;
        double m_dSaturation;
        double m_dLightness;
        double m_dTransparency;
        Color m_oColorRGB;

    }
}
